/*
 * Copyright (C) 2012 DataSymphony (http://datasymphony.com.au)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package au.com.ds.restty.js;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;

import au.com.ds.restty.exc.LogicError;

public class JsObjectFormatter {
	private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
	private static final SimpleDateFormat ISO_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
	
	private Set<JsObjectType> mExpectedTypes = new HashSet<JsObjectType>();
	private JavaScriptEngine mJavaScript;
	private boolean mOptional;
	private boolean mSupportsExpressions = false;
	
	private JsObjectFormatter() {
		
	}
	
	public static JsObjectFormatter optional() {
		JsObjectFormatter fmt = new JsObjectFormatter();
		fmt.mOptional = true;
		return fmt;
	}
	
	public static JsObjectFormatter required() {
		JsObjectFormatter fmt = new JsObjectFormatter();
		fmt.mOptional = false;
		return fmt;
	}
	
	public JsObjectFormatter expects(JsObjectType type) {
		mExpectedTypes.add(type);
		return this;
	}
	
	public JsObjectFormatter supportsExpressions(JavaScriptEngine js){
		mJavaScript = js;
		mSupportsExpressions = true;
		return this;
	}
	
	public String format(JsAnyObject o) {
		if (o == null){
			return "";
		}
		
		if (!mExpectedTypes.isEmpty()) {
			if (mOptional) {
				mExpectedTypes.add(JsObjectType.UNDEFINED);
			}
			
			if (mSupportsExpressions) {
				mExpectedTypes.add(JsObjectType.FUNCTION);
			}
		}
		
		if (!mExpectedTypes.isEmpty() && !mExpectedTypes.contains(o.getType())){
			throw new LogicError("Invalid object type: " + o.getType() + "; expected: " + mExpectedTypes);
		}
		
		switch (o.getType()) {
		case DOUBLE:			
			JsDouble d = (JsDouble) o;
			return DECIMAL_FORMAT.format(d.getValue());
		case NULL:
			return "";
		case STRING:
			JsString s = (JsString) o;
			return s.getValue();
		case UNDEFINED:			
			return "";
		case BOOLEAN:
			JsBoolean b = (JsBoolean) o;
			return b.getValue() ? "true" : "false";
		case DATE:
			JsDate dt = (JsDate) o;
			return ISO_DATE_FORMAT.format(dt.getValue());
		case FUNCTION:
			if (mJavaScript == null){
				throw new LogicError("JavaScriptEngine has to be specified");
			}
			
			JsFunction f = (JsFunction) o;
			return format(mJavaScript.call(f));
		default:
			throw new LogicError("Unsupported object type: " + o.getType());
		}
		
	}
}
